package com.glor.operation.common.repo;

import com.glor.operation.common.Constants;
import com.glor.operation.common.entity.BaseEntity;
import com.glor.operation.common.utils.RedisCacheHelper;
import com.glor.operation.common.utils.SpringContextHolder;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.JpaEntityInformationSupport;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.util.Assert;

import javax.persistence.Cacheable;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

/**
 * Created by heylear.cai on 2016/2/18.
 */
@NoRepositoryBean
public class BaseRepositoryImpl<T extends BaseEntity, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements BaseRepository<T, ID> {
    private final EntityManager em;

    public BaseRepositoryImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
        super(entityInformation, entityManager);
        this.em = entityManager;
    }

    public BaseRepositoryImpl(Class<T> domainClass, EntityManager em) {
        this(JpaEntityInformationSupport.getMetadata(domainClass, em), em);
    }

    @Override
    public void delete(T entity) {
        entity.setIsDelete(Constants.SC_DELETED);
        this.save(entity);
    }

    @Override
    public <S extends T> S save(S entity) {
        Assert.notNull(entity, "entity can't be null!");
        Date now = Calendar.getInstance().getTime();
        if (entity.getId() == null){
            entity.setCreateTime(now);
            entity.setCreateUser(SpringContextHolder.getCurrentUserId());
            entity.setIsDelete(Constants.SC_UN_DELETE);
        }
        entity.setUpdateUser(SpringContextHolder.getCurrentUserId());
        entity.setUpdateTime(now);
        return super.save(entity);
    }

    @Override
    public T findOne(ID id) {
        return super.findOne((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(criteriaBuilder.equal(root.get("id"), id));
            predicates.add(criteriaBuilder.equal(root.get("isDelete"), Constants.SC_UN_DELETE));
            criteriaQuery.where(predicates.toArray(new Predicate[]{}));
            return null;
        });
    }

    @Override
    public List<T> findAll() {

        CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();

        CriteriaQuery<T> criteriaQuery = criteriaBuilder.createQuery(getDomainClass());

        Root<T> root = criteriaQuery.from(getDomainClass());

        criteriaQuery.where(criteriaBuilder.equal(root.get("isDelete"), Constants.SC_UN_DELETE));

        Query query = em.createQuery(criteriaQuery);

        query.setHint(Constants.ORG_HIBERNATE_CACHEABLE, isCacheable());

        return query.getResultList();
    }

    @Override
    public Page<T> findAll(Pageable pageable) {
        return super.findAll((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(criteriaBuilder.equal(root.get("entityStatus"), 1));
            criteriaQuery.where(predicates.toArray(new Predicate[]{}));
            return null;
        }, pageable);
    }

    protected boolean isCacheable() {
        return this.getDomainClass().getAnnotation(Cacheable.class) != null;
    }
}
